/*
 * Matthew Cramer, Jesse Martinez, Marc Mendoza 
 * CSE 325 Lab 3
 * File: QueueManager.c
 * Description: Manager that handles calls to the Queue.  This is called from
 *              the Process manager to perform the functions to the stack.
 *
 */

#include "QueueManager.h"
#include "../ProcessManager/ProcessManager.h"
#define NUM_REGS 3
#define MAX_PROCESSES 20
#define MAX_QUEUE_SIZE 20
#define MAX_RUNNING_SIZE 1


/*
 * Declare functions
 */
void Enqueue(int, int, int, int, int, int, int, int, int, int*, int*, int*, int*, process_control_block*);
process_control_block Dequeue(int*, int*, int*, int*, process_control_block*);
process_control_block Delete(int, int*, int*, int*, int*, int*, process_control_block*, process_control_block*);
void iniFree(int*, int*, process_control_block*);
void iniQueue(int*, int*, int*, int*, int*, process_control_block*, int);

/*
 * Enqueue: Receives information from Testing Interface to put onto stack
 */
void Enqueue(int processNumber, int psw, int pageTable, 
             int reg0, int reg1, int reg2, int priority, int TQ, int group,
             int* head, int* tail, int* freeHead, int* freeTail, 
             process_control_block* processQueue)
{    
    /*
     * Pops the head of the free list to use and updates the pointer to freeHead
     */
    //Used for when queue has reached max limit, reset free list pointer
    if(*freeHead == -1 && *freeTail == MAX_QUEUE_SIZE-1)
    {
        *freeHead = 0;
    }
    
	//enqueues onto an empty queue
    if(*head==-1 && *tail==-1)
   // if(*head==*tail)
    {
        *head = *freeHead;
		*tail = *freeHead;
		*freeHead = processQueue[*freeHead].nextIndex;
       // *freeHead = *freeTail;
		processQueue[*freeHead].prevIndex = -1;
		processQueue[*tail].nextIndex = -1;
    }

	//enqueues onto a partially filled queue
	else
	{
		//if(*freeHead == *freeTail)
		//	*freeTail = -1;
		processQueue[*tail].nextIndex = *freeHead;
		processQueue[*freeHead].prevIndex = *tail;
		*tail = *freeHead;
		*freeHead = processQueue[*freeHead].nextIndex;
		processQueue[*freeHead].prevIndex = -1;
		processQueue[*tail].nextIndex = -1;
        //If free list has been used up, reset pointer
        if(*freeHead == -1)
        {
            *freeHead = *freeTail;
        }
	}
	
    /*
     * Insert paramaters into struct at open slot
     */
    processQueue[*tail].validBit = 1;
    processQueue[*tail].pid = processNumber;
    processQueue[*tail].psw = psw;
    processQueue[*tail].pageTable = pageTable;
    processQueue[*tail].regs[0] = reg0;
    processQueue[*tail].regs[1] = reg1;
    processQueue[*tail].regs[2] = reg2;
    processQueue[*tail].priority = priority;
    processQueue[*tail].TQ = TQ;
    processQueue[*tail].group = group;
    processQueue[*tail].counter = 0;
}

/*
 * Dequeue: returns the PID at head of Queue and Deletes it, -1 if no elements
 */
process_control_block Dequeue(int* head, int* tail, int* freeHead, int* freeTail, process_control_block* queue)
{
	int temp = *head;
	if( *head == *tail)//sets head and tail to null if there is only one block
	{
		*tail = -1;
		*head = -1;
	}
	else
		*head = queue[*head].nextIndex;

	queue[temp].validBit = 0;
	
	if(*freeHead == 0)//if there is nothing on free list
	{
		*freeHead == temp;
		*freeTail == temp;
		queue[*freeTail].nextIndex = -1;
		queue[temp].prevIndex = -1;
	}
	else
	{
        //If free list hits max count, reset head of free list to be used again
        if(*freeHead == -1)
        {
            *freeHead = *freeTail;
        }
        //move free slot to free list
		queue[*freeTail].nextIndex = temp;
		queue[temp].prevIndex = *freeTail;
		queue[temp].nextIndex = -1;
		*freeTail = temp;
	}
	return queue[temp];
}

/*Delete: Receives the pid of the process to be deleted.
*		Returns the pid when delete is a successful and -2 if pid is not found.
*/
process_control_block Delete(int processNumber, int* head, int* tail, int* freeHead, int* freeTail, int* queueSize, process_control_block* processQueue, process_control_block* tempPcb)
{
	int i = *head; //variable to iterate through the list
	while(i != -1)
	{
		if(processQueue[i].pid == processNumber)
		{
			if(i==*head)
			{
				*head = processQueue[i].nextIndex; //head is changed to the next item in the list
                if(*queueSize == 1)
                {
                    *tail=*head;
                    
                 //   processQueue[*tail].prevIndex = -1; //the new heads' prevIndex is reset
                }
                else
                {
                    processQueue[*head].prevIndex = -1; //the new heads' prevIndex is reset
                }
			}
			else if(i==*tail)
			{
				*tail = processQueue[i].prevIndex; //tail is changed to the previous item in the list
				processQueue[*tail].nextIndex = -1; //the new tails' nextIndex is reset
			}
			else//if the item is in the middle of the list
			{
				//the previous is' next is set to is' next
				processQueue[processQueue[i].prevIndex].nextIndex = processQueue[i].nextIndex;
				//the next items' previous is set to is' previous
				processQueue[processQueue[i].nextIndex].prevIndex = processQueue[i].prevIndex;
			}
            //If free list hits max count, reset head of free list to be used again
            if(*freeHead == -1)
            {
                *freeHead = *freeTail;
            }
			//the item taken out is added to the end of the freelist
			processQueue[i].nextIndex = -1;
			processQueue[i].prevIndex = *freeTail;
			processQueue[*freeTail].nextIndex = i;
			*freeTail = i;
			
			//valid bit is reset
			processQueue[i].validBit = 0;
			tempPcb[0] = processQueue[i];
			return tempPcb[0];
		}
		
		i = processQueue[i].nextIndex;
	}
	//return error of not found
	tempPcb[0].pid = -2;
	
	//returns no existing process error
	return tempPcb[0];
}

//Reset the Free List for the given Queue
void iniFree(int* head, int* tail, process_control_block* processQueue)
{
	int i;
	for(i = 0; i < *tail; i++)
	{
		processQueue[i].prevIndex = i-1;
		processQueue[i].nextIndex = i+1;
	}
	processQueue[*tail].prevIndex = i-1;
	processQueue[*tail].nextIndex = -1;
}

//Clears all processes in queue
void iniQueue(int* head, int* tail, int* freeHead, int* freeTail, int* size, process_control_block* queue, int max)
{
    *head=-1;
    *tail=-1;
    *freeHead = 0;
    *freeTail = max-1;
    *size = 0;
    int i=0;
    for(i=0; i<max; i++)
    {
        queue[i].validBit = 0;
    }
}

